#include "do_instruct.h"
#include "operations.h"

/********************************************
 *
 *	File: 		do_instruct.c
 *	Name: 		Rob King
 *	Project:	Project 2
 *
 ********************************************/

int doinstruct(VMEMMOD* vm, int clk, int* pid) {
	int IC = 0;
	int retVal = 0;
	int x;
	char instruction[4];
	bop pot = 0;
	IC = disasm(vm->mm->pcb[vm->mm->nCurrProc - 1].IC);
	vmem_read(vm, IC, instruction, vm->mm->nCurrProc);
	(IC)++;
	diss(IC, (vm->mm->pcb[vm->mm->nCurrProc - 1]).IC);

	binst instruc = (binst)((instruction[0] << 8) | (instruction[1]));

	size_t o3 = atob2(instruction[2], instruction[3]);
	char temp[5] = {0};
	temp[2] = instruction[2];
	temp[3] = instruction[3];
	u_int16_t c3 = instruction[2] << 8 | instruction[3];
	//dump_mem(mm);

#ifdef	GRAPHICAL
	/* Do some busy waiting */
	while(!_CanContinue()) ;
#endif
#ifdef DEBUG
		dump_instruc(instruction, IC-1, vm->mm ,clk);
#endif
		switch(instruc) {
				case LR:
				/* R := [a] */
				/* Load Register */
				LoadRegister(vm, o3);
				break;
				case LL:
				/* R := zz[a,3][a,4] */
				/* Load low bytes */
				LoadLow(vm, o3);
				break;
				case LH:
				/* R := [a,1][a,2]zz */
				/* Load high bytes */
				LoadHigh(vm, o3);
				break;
				case SR:
				/* [a] := R */
				/* Store register */
				StoreRegister(vm, o3);
				break;
				case SP:
				/* SP := R */
				/* Stack Pointer */
				SetStackPointer(vm, o3);
				break;
				case PS:
				/* R:= SP */
				/* Put stack pointer */
				StoreStackPointer(vm);
				break;
				case PH:
				/* [SP] := R; SP++ */
				/* Push stack */
				PushStack(vm);
				break;
				case PP:
				/* [SP] := R; SP++ */
				/* Pop stack */
				PopStack(vm);
				break;
				case CE:
				/* if R = [a] then C := 'T' else C := 'F' */
				/* Compare equal */
				CompareEqual(vm, o3);
				break;
				case CL:
				/* The following comment might be wrong */
				/* if R = [a] then C := 'T' else C := 'F' */
				/* Compare less than */
				CompareLessThan(vm, o3);
				break;
				case BT:
				/* if C = 'T' then IC := a */
				/* Branch conditional */
				BranchConditional(vm, o3);
				break;
				case BU:
				/* IC := a */
				/* Branch unconditional */
				BranchUnconditional(vm, o3);
				break;
				case GD:
				/* Read( [b+i], I = 0,...,9) */
				/* Get data */
				GetData(vm, o3);
				break;
				case PD:
				/* The following comment may be wrong */
				/* Read( [b+i], I = 0,...,9) */
				/* Put data */
				PutData(vm, o3);
				break;
				case AD:
				/* R := R + [a] */
				/* Add */
				Add(vm, o3);
				break;
				case SU:
				/* R := R - [a], R >= [a] */
				/* Subtract */
				Subtract(vm, o3);
				break;
				case MU:
				/* R := R * [a] */
				/* Multiply */
				Multiply(vm, o3);
				break;
				case DI:
				/* R := R / [a], [a] > 0 */
				/* Divide */
				Divide(vm, o3);
				break;
				case AS:
				/* Temp := [SP] ; SP- - ; [SP] := [SP] + Temp */
				/* Add stack */
				AddStack(vm);
				break;
				case SS:
				/* Temp := [SP] ; SP- - ; [SP] := [SP] - Temp */
				/* Subtract stack */
				SubtractStack(vm); //character representations.
				break;
				case MS:
				/* Temp := [SP] ; SP- - ; [SP] := [SP] * Temp */
				/* Multiply stack */
				MultiplyStack(vm);
				break;
				case DS:
				/* Temp := [SP] ; SP- - ; [SP] := [SP] / Temp */
				/* Divide stack */
				DivideStack(vm);
				break;
				case NP:
				/* NULL operation */
				/* No-op */
				NoOp(vm);
				break;
				case SD:
				/* Send data in a message to a process */
				/* SendMess(MEMMOD* mm, u_int8_t nToProc) */
				SendMess(vm, o3);
				retVal = 3;
				break;
				case RC:
				/* Recieve data in a message from a process */
				/* int mdequeue(MSQUEUE *q, char *mess); */
				retVal = ReceiveMess(vm, c3);
				if (retVal == -1)
				{
					(IC)--;
					diss(IC, (vm->mm->pcb[vm->mm->nCurrProc - 1]).IC);
					retVal = 2;
				}else
					retVal = 4;
				/* Check ret */
				break;
				case GP:
				/* Store the current process ID in the register */
				/* PutPID(MEMMOD* mm, u_int8_t nPID) */
				PutPID(vm, o3);
				break;
				case FK:
					Fork(vm);
					(*pid) = disasm(vm->mm->pcb[vm->mm->nCurrProc - 1].reg);
					break;
				case EX:
					Exec(vm, instruction[2], instruction[3]);
					break;
				case LS:
					LoadShared(vm, o3);
					break;
				case ST:
					StoreShared(vm, o3);
					break;
				case PE:
					x = P(vm, o3);
					if (x<0) {
						retVal = 3;
					}
					break;
				case VE:
					x = V(vm, o3);
					diss(x, vm->mm->pcb[vm->mm->nCurrProc - 1].reg);
					if (x>0) {
						retVal = 4;
					}
					break;
				case SI:
					SemaphoreInit(vm, o3);
					break;
				case H:
				/* Halt */
				Halt(vm);
				retVal = 1;
				break;
				default :
				printf("Invalid instruction received %x @ IC = %d\n", instruc,disasm(vm->mm->pcb[vm->mm->nCurrProc - 1].IC));
			break;
		}

	return retVal;
}

int atob2(bop op1, bop op2) {
	int retval = -1;
	if (op1 != 0 && op2 != 0) {
		char a[3] = { 0 };
		a[0] = (char) op1;
		a[1] = (char) op2;
		a[2] = '\0';
		retval = atoi(a);
	}
	return retval;
}

void dump_instruc(char * instruc, size_t IC, MEMMOD* mm, int clk) {

	char s[5], r[5];
	s[0] = instruc[0];
	s[1] = instruc[1];
	s[2] = instruc[2];
	s[3] = instruc[3];
	s[4] = '\0';
	r[0] = mm->pcb[mm->nCurrProc - 1].reg[0];
	r[1] = mm->pcb[mm->nCurrProc - 1].reg[1];
	r[2] = mm->pcb[mm->nCurrProc - 1].reg[2];
	r[3] = mm->pcb[mm->nCurrProc - 1].reg[3];
	r[4] = '\0';

	printf("Clock: %d \t Process #%d; \t IC:%d \t Instruc %s \t R: %s\n",clk, mm->nCurrProc, IC,
			s, r);

}

